// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#ifndef __PLANETARYATMOSPHERE_H_
#define __PLANETARYATMOSPHERE_H_

#include <vector>
#include <OgreSceneNode.h>

namespace Ogre
{
	class PlanetaryAtmosphereRenderable;
	class PlanetarySceneManager;
	class PlanetaryOptions;
	class PlanetaryPlanet;
	class PlanetaryPage;

	/**
	 * Provides a simple atmosphere based on a cube-mapped sphere with LOD-options.
	 * Will change the materials of the planet when needed (entering or leaving the atmosphere).
	 * Will not be created unless the "Atmosphere"-option is set to true in the planet's config file.
	*/
	class PlanetaryAtmosphere
	{
		public:
			PlanetaryAtmosphere(PlanetarySceneManager *mgr);
			~PlanetaryAtmosphere();

			void init(PlanetaryPlanet *parent);
			void load();
			void unload();
			void uninit();

			inline SceneNode *getSceneNode()
			{
				return mSceneNode;
			}

			/** Set the material to be used by the atmosphere and sub-entities
			 *  @note will use the same format as the shader materials, ie, the name supplied will have "skyFromSpace" etc appended
			 *  @note undefined effect if material does not exist
			 */
			void setMaterial(String matname);

			inline PlanetaryPlanet *getParent()
			{
				return mParent;
			}
			
			/// collection of parametres that can be set in the shader
			struct shaderParams
			{
				Ogre::Real mInnerRadius;
				Ogre::Real mOuterRadius;
				Ogre::Real mOuterRadiusSqr;
		
				Ogre::Real mScale;
				Ogre::Real mScaleDepth;
				Ogre::Real mScaleOverScaleDepth;
				
				Ogre::Vector3 mWaveLengths;
				Ogre::Vector3 mInvWaveLengthsPow4;
				Ogre::Real mKr;
				Ogre::Real mKm;
				Ogre::Real mKr4PI;
				Ogre::Real mKm4PI;
				Ogre::Real mESun;
				Ogre::Real mKrESun;
				Ogre::Real mKmESun;
				Ogre::Real mDepth;
				Ogre::Real mExposure;
			};
			
			/// returns the current set of shader-parametres
			const shaderParams getShaderParams() const;
			/// sets the shader parametres to the given parametres
			void setShaderParams(shaderParams &params);
			/// writes the parametres to the shader
			void updateShader();
			/// sets whether the camera is inside the atmosphere or not
			void setInAtmosphere(bool inAtmo);

		private:
			PlanetarySceneManager 	*mSceneMgr;
			PlanetaryOptions	*mOptions;
			PlanetaryPlanet		*mParent;

			/// Renderable for atmosphere
			PlanetaryAtmosphereRenderable *mRenderable[6];

			/// Scene node for atmosphere
			SceneNode *mSceneNode;
			/// Scene nodes for renderables
			SceneNode *mRenderableNode[6];

			/// This zone index
			//unsigned int mZone;

			/// Pages of this zone
			//std::vector< std::vector<PlanetaryPage *> > mPages;

			bool mIsInit;
			bool mIsLoaded;
			
			/// are we inside the atmosphere?
			bool mInAtmosphere;
			
			/// updates the shader-variables that are based on calculations
			inline void updateCalculations()
			{
				mInvWaveLengthsPow4.x = 1.0f / Math::Pow(mWaveLengths.x,4.0f);
				mInvWaveLengthsPow4.y = 1.0f / Math::Pow(mWaveLengths.y,4.0f);
				mInvWaveLengthsPow4.z = 1.0f / Math::Pow(mWaveLengths.z,4.0f);
	
				// This must stay this way as some of the constants
				// as the depth scale function relies on this.
				mOuterRadius = mInnerRadius * 1.025;
	
				mOuterRadiusSqr = mOuterRadius * mOuterRadius;
	
				mKr4PI = mKr * 4.0f * Ogre::Math::PI;
				mKm4PI = mKm * 4.0f * Ogre::Math::PI;
	
				mKrESun = mKr * mESun;
				mKmESun = mKm * mESun;
	
				mScale = 1.0f / (mOuterRadius - mInnerRadius);
				mScaleOverScaleDepth = mScale / mScaleDepth;
			}
			
			/// Materials used
			Ogre::MaterialPtr mGroundFromAtmosphere;
			Ogre::MaterialPtr mGroundFromSpace;
			Ogre::MaterialPtr mSkyFromAtmosphere;
			Ogre::MaterialPtr mSkyFromSpace;
			
			/// Shader variables
			Ogre::Real mInnerRadius;
			Ogre::Real mOuterRadius;
			Ogre::Real mOuterRadiusSqr;
		
			Ogre::Real mScale;
			Ogre::Real mScaleDepth;
			Ogre::Real mScaleOverScaleDepth;

			Ogre::Vector3 mWaveLengths;
			Ogre::Vector3 mInvWaveLengthsPow4;
			Ogre::Real mKr;
			Ogre::Real mKm;
			Ogre::Real mKr4PI;
			Ogre::Real mKm4PI;
			Ogre::Real mESun;
			Ogre::Real mKrESun;
			Ogre::Real mKmESun;
			Ogre::Real mDepth;
			Ogre::Real mExposure;
	};
}

#endif //#ifndef __PLANETARYATMOSPHERE_H_
